#pragma once

#include "vector.h"

namespace g2 {

template <typename T>
class basic_box;

typedef basic_box<double> box;
typedef basic_box<float> boxf;
typedef basic_box<long double> boxl;

template <typename T>
class basic_box {
public:
	T x1, y1, x2, y2;
public:
	basic_box()
		{ }
	basic_box(T _x1, T _y1, T _x2, T _y2)
		: x1(_x1), y1(_y1), x2(_x2), y2(_y2) { }
	basic_box(const basic_vector<T> & min, const basic_vector<T> & max)
		: x1(min.x), y1(min.y), x2(max.x), y2(max.y) { }
	explicit basic_box(const basic_vector<T> & p)
		: x1(p.x), y1(p.y), x2(p.x), y2(p.y) { }
	basic_box(const basic_box & b) 
		: x1(b.x1), y1(b.y1), x2(b.x2), y2(b.y2) { }
	template <class U> 
	basic_box(const basic_box<U> & b) 
		: x1(T(b.x1)), y1(T(b.y1)), x2(T(b.x2)), y2(T(b.y2)) { }
public:
	basic_vector<T> min() const
		{ return basic_vector<T>(x1, y1); }
	basic_vector<T> max() const
		{ return basic_vector<T>(x2, y2); }
	basic_vector<T> center() const
		{ return (min() + max()) / T(2); }
	bool inside(const basic_vector<T> & v) const
		{ return min() <= v && v < max(); }
	bool empty() const
		{ return !(min() < max()); }
	bool operator == (const basic_box & b) const
		{ return x1 == b.x1 && y1 == b.y1 && x2 == b.x2 && y2 == b.y2; }
	bool operator != (const basic_box & b) const
		{ return x1 != b.x1 || y1 != b.y1 || x2 != b.x2 || y2 != b.y2; }
	T width() const
		{ return x2 - x1; }
	T height() const
		{ return y2 - y1; }
	basic_vector<T> size() const
		{ return basic_vector<T>(width(), height()); }
	T area() const
		{ return width() * height(); }
	basic_box & normalize() {
		if (x2 < x1)
			std::swap(x1, x2);
		if (y2 < y1)
			std::swap(y1, y2);
		return * this;
	}
	basic_box & inflate(T dx, T dy) {
		x1 -= dx;
		y1 -= dy;
		x2 += dx;
		y2 += dy;
		return * this;
	}
	basic_box & inflate(T d) 
		{ return inflate(d, d); }
	basic_box & offset(T dx, T dy) {
		x1 += dx;
		y1 += dy;
		x2 += dx;
		y2 += dy;
		return * this;
	}
	basic_box & inflate(const basic_vector<T> & d)
		{ return inflate(d.x, d.y); }
	basic_box & include(const basic_vector<T> & v) {
		if (x1 > v.x)
			x1 = v.x;
		if (y1 > v.y)
			y1 = v.y;
		if (x2 < v.x)
			x2 = v.x;
		if (y2 < v.y)
			y2 = v.y;
		return * this;
	}
	basic_box & include(const basic_box<T> & v) 
		{ return include(v.min()).include(v.max()); }
	void limit(basic_vector<T> & p) const {
		assert (!empty());
		if (p.x < x1) p.x = x1;
		if (p.y < y1) p.y = y1;
		if (p.x > x2) p.x = x2;
		if (p.y > y2) p.y = y2;
	}
};

} // g2
